package by.hqsoftware.RollingBall.GraphicElements;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.microedition.khronos.opengles.GL10;

import by.hqsoftware.RollingBall.Converter;

import android.content.res.Resources;

public abstract class Mesh {
	// Our vertex buffer.
    private FloatBuffer verticesBuffer = null;
 
    // Our index buffer.
    private ShortBuffer indicesBuffer = null;
    
    private FloatBuffer normalVerticesBuffer = null;      
 
    // The number of indices.
    private int numOfIndices = 0;    
 
    // Flat Color
    private float[] rgba = new float[]{1.0f, 1.0f, 1.0f, 1.0f};
 
    // Smooth Colors
    private FloatBuffer colorBuffer = null;
    
    // Translate params.
    public float x = 0;
    public float y = 0;
    public float z = 0;
 
    // Rotate params.
    public float rx = 0;
    public float ry = 0;
    public float rz = 0;
    
    protected abstract void customDraw(GL10 gl);
    
    public void draw(GL10 gl) {
    	// Counter-clockwise winding.
		gl.glFrontFace(GL10.GL_CCW);
		// Enable face culling.
		gl.glEnable(GL10.GL_CULL_FACE);
		// What faces to remove with the face culling.
		gl.glCullFace(GL10.GL_BACK);
		// Enabled the vertices buffer for writing and to be used during
		// rendering.
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		//enable normals buffer
		gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
		// Specifies the location and data format of an array of vertex
		// coordinates to use when rendering.
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, verticesBuffer);
		// Set flat color
		gl.glColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
		// Smooth color
		if (colorBuffer != null) {
			// Enable the color array buffer to be used during rendering.
			gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
			gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
		}

		gl.glPushMatrix();
		gl.glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
		gl.glTranslatef(x, y, z);
		gl.glRotatef(rx, 1, 0, 0);
		gl.glRotatef(ry, 0, 1, 0);
		gl.glRotatef(rz, 0, 0, 1);				

		gl.glNormalPointer(GL10.GL_FLOAT, 0, normalVerticesBuffer);
		
		customDraw(gl);
		
		// Point out the where the color buffer is.
		gl.glDrawElements(GL10.GL_TRIANGLES, numOfIndices,
				GL10.GL_UNSIGNED_SHORT, indicesBuffer);
		
		gl.glPopMatrix();
		// Disable the vertices buffer.
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		// disable normal buffer
		gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
		// Disable face culling.
		gl.glDisable(GL10.GL_CULL_FACE);
    }
    
    
    
    protected void setVertices(float[] vertices) {
    	verticesBuffer = Converter.toBuffer(vertices);
    }
     
    protected void setIndices(short[] indices) {
    	indicesBuffer = Converter.toBuffer(indices);
    	numOfIndices = indices.length;
    }
    
    protected void setNormalVertices(float[] vertices) {
    	normalVerticesBuffer = Converter.toBuffer(vertices);
    }
                            
     
    protected void setColor(float red, float green, float blue, float alpha) {
            // Setting the flat color.
            rgba[0] = red;
            rgba[1] = green;
            rgba[2] = blue;
            rgba[3] = alpha;
    }
     
    protected void setColors(float[] colors) {
    	colorBuffer = Converter.toBuffer(colors);
    }
   	
    
    public void parse(Resources resources, String name, int objId) {
    	
    	final String VERTEX = "v";
        final String FACE = "f";    
        final String OBJECT = "o";
        final String VERTEX_NORMAL = "vn";
        
		 InputStream objFile = resources.openRawResource(objId);
		 BufferedReader buffer = new BufferedReader(new InputStreamReader(objFile));
		 String line;

		 ArrayList<Float> vertices = new ArrayList<Float>();		 
		 ArrayList<Short> indices = new ArrayList<Short>();
		 
		 ArrayList<Float> normalVertices = new ArrayList<Float>();	
		 
		 
		 boolean objectFounded = false;

		 try {
			while ((line = buffer.readLine()) != null) {
			    StringTokenizer parts = new StringTokenizer(line, " ");
			    int numTokens = parts.countTokens();
			    if (numTokens == 0)
			            continue;
			    String type = parts.nextToken();

			    if (type.equals(OBJECT)) {			    	
			    	String objName = parts.hasMoreTokens() ? parts.nextToken() : "";
			    	objectFounded = objName.compareToIgnoreCase(name) == 0;
			    }
			    if (!objectFounded)
			    	continue;
			                    
			    if (type.equals(VERTEX)) { 
			    	vertices.add(Float.parseFloat(parts.nextToken()));
			    	vertices.add(Float.parseFloat(parts.nextToken()));
			    	vertices.add(Float.parseFloat(parts.nextToken()));
			    } else if (type.equals(FACE)) {
			    	while (parts.hasMoreTokens()) {			    		
		    			StringTokenizer subParts = new StringTokenizer(parts.nextToken(), "/");		    			
		    			
		    			for (int i = 0; i < 1; i++) {
		    				String subPart = subParts.nextToken();
		    				if (subPart.isEmpty())
		    					continue;
		    				indices.add((short) (Short.parseShort(subPart) - (short)1));
		    			}			    					    	
					}            	                
			    } else if (type.equals(VERTEX_NORMAL)) {
			    	while (parts.hasMoreTokens()) {
			    		normalVertices.add(Float.parseFloat(parts.nextToken()));
			    	}
			    }
			 }
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}		
		 		 
		 setVertices(Converter.ToFloatArray(vertices));
		 setIndices(Converter.ToShortArray(indices));
		 setNormalVertices(Converter.ToFloatArray(normalVertices));		 		 
    }       
}
